package com.xmlt.core.template;

import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import ognl.OgnlException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.xmlt.core.template.contentgenerators.GeneratorRegistry;
import com.xmlt.core.template.contentgenerators.IContentGenerator;
import com.xmlt.core.template.instance.AbstractInstance;
import com.xmlt.core.template.instance.Instance;
import com.xmlt.core.template.instance.InstanceFactory;

public class Template {
	public static final String SECTION_NAME_DISPLAY = "display";
	
	private TemplateManager manager;
	private String reference;
	private List<TemplateParameter> parameters;
	private List<TemplateSection> sections;
	private IContentGenerator generator;
	private String generatorId;
	private Document templateDocument;

	public Template(Document d, String ref, TemplateManager tm)
			throws TemplateException {
		manager = tm;
		reference = ref;
		parameters = new ArrayList<TemplateParameter>();
		sections = new ArrayList<TemplateSection>();
		templateDocument = d;
		initialize(d);
	}

	public String getReference() {
		return reference;
	}
	
	public Document getdocument() {
		return templateDocument;
	}
	
	protected TemplateSection getSection(String name) {
		for (TemplateSection ts : sections) {
			if (ts.getName().equals(name)) {
				return ts;
			}
		}
		return null;
	}

	public TemplateSection getDisplaySection() {
		return getSection(SECTION_NAME_DISPLAY);
	}

	public Instance generateInstance() throws TemplateException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		Document instance = null;
		try {
			DocumentBuilder db = dbf.newDocumentBuilder();
			instance = db.newDocument();
		} catch (ParserConfigurationException e) {
			throw new TemplateException(e);
		}
		if (instance != null) {
			Element instanceEl = generateChildInstances(instance);
			instanceEl.setAttribute("templateRef", reference);
			return (Instance) InstanceFactory.newInstance(instance, manager, null);
		}
		return null;
	}

	public Element generateChildInstances(Node n) {
		Document d = null;
		if (n.getNodeType() == Node.DOCUMENT_NODE) {
			d = (Document) n;
		} else {
			d = n.getOwnerDocument();
		}
		Element instanceEl = d.createElement("instance");
		n.appendChild(instanceEl);
		for (TemplateParameter tp : parameters) {
			tp.generateParameterInInstanceNode(instanceEl);
		}
		return instanceEl;
	}

	public List<TemplateParameter> getParameters() {
		return parameters;
	}
	
	public TemplateParameter getParameter(String id) {
		for (TemplateParameter tp: parameters) {
			if (tp.getName().equals(id))
				return tp;
		}
		return null;
	}

	public List<String> getSectionNames() {
		List<String> names = new ArrayList<String>();
		for (TemplateSection ts : sections) {
			names.add(ts.getName());
		}
		return names;
	}

	public List<TemplateSection> getSections() {
		return sections;
	}

	public String toString() {
		String s = "";
		s += "parameters\n";
		for (TemplateParameter tp : parameters)
			s += tp;
		s += "\nsections\n";
		for (TemplateSection ts : sections)
			s += ts;
		return s;
	}
	
	public IContentGenerator getGenerator() {
		return generator;
	}
	
	public String getGeneratorId() {
		return generatorId;
	}

	private void initialize(Document doc) throws TemplateException {
		NodeList nl = doc.getDocumentElement().getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
			Node n = nl.item(i);
			if (n.getNodeType() == Node.ELEMENT_NODE) {
				Element el = (Element) n;
				if (n.getNodeName().equals("parameter")) {
					initializeParam(el);
				} else if (n.getNodeName().equals("generator")) {
					initializeGenerator(el);
				} else if (n.getNodeName().equals("section")) {
					initializeSection(el);
				}
			}
		}
	}
	
	private void initializeGenerator(Element el) throws TemplateException {
		generatorId = el.getAttribute("id");
		if (generatorId == null)
			return;
		
		try {
			generator = GeneratorRegistry.getGenerator(generatorId);
		} catch (InstantiationException e) {
			throw new TemplateException(e);
		} catch (IllegalAccessException e) {
			throw new TemplateException(e);
		}
	}

	private void initializeParam(Element el) {
		parameters.add(new TemplateParameter(el, manager));
	}

	private void initializeSection(Element el) throws TemplateException {
		sections.add(new TemplateSection(el));
	}

	public TemplateParameter lookupParameter(String paramName) {
		TemplateParameter tp = null;
		for (TemplateParameter p : parameters) {
			if (p.getName().equals(paramName)) {
				tp = p;
				break;
			}
		}
		return tp;
	}
}
